/*
 * =====================================================================================
 *
 *       Filename:  mygenhsv.c
 *
 *    Description:  gen hsv and save bmp
 *    				1. gen yuv 422
 *    				2. yuv422 to rgb888 and save bmp check the convert
 *    				3. rgb8888 to hsv and save hsv to bmp
 *
 *        Version:  1.0
 *        Created:  2017年06月27日 09时26分54秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  yangkun (yk)
 *          Email:  xyyangkun@163.com
 *        Company:  yangkun.com
 *
 * =====================================================================================
 */

#include <stdlib.h>
#include <stdio.h>
/**
 * Convert RGB24 format to BMP format
 *
 * @param rgb24path     path of input RGB24 file.
 * @param bmppath       path of output BMP file
 * @param width         the width of picture.
 * @param height        the height of picture.
 * @return 0 if finished, -1 if there are errors.
 */
int rgb24_to_bmp(char *rgb24path,char *bmppath,int width,int height)
{
    typedef struct
    {  
        unsigned int imageSize;
        unsigned int blank;
        unsigned int startPosition;
    }BmpHead;
 
    typedef struct
    {
        unsigned int  Length;
        unsigned int  width;
        unsigned int   height;
        unsigned short  colorPlane;
        unsigned short  bitColor;
        unsigned int  zipFormat;
        unsigned int  realSize;
        unsigned int  xPels;
        unsigned int  yPels;
        unsigned int  colorUse;
        unsigned int  colorImportant;
    }InfoHead;
     
    int i=0,j=0;
    BmpHead m_BMPHeader={0};
    InfoHead  m_BMPInfoHeader={0};
    char bfType[2]={'B','M'};
    int header_size=sizeof(bfType)+sizeof(BmpHead)+sizeof(InfoHead);
    unsigned char *rgb24_buffer=NULL;
    FILE *fp_rgb24=NULL,*fp_bmp=NULL;
     
    if((fp_rgb24=fopen(rgb24path,"rb"))==NULL){
        printf("Error: Cannot open input RGB24 file.\n");
        return -1;
    }
    if((fp_bmp=fopen(bmppath,"wb"))==NULL){
        printf("Error: Cannot open output BMP file.\n");
        return -1;
    }
     
    rgb24_buffer=(unsigned char *)malloc(width*height*3);
    fread(rgb24_buffer,1,width*height*3,fp_rgb24);
 
    m_BMPHeader.imageSize=3*width*height+header_size;
    m_BMPHeader.startPosition=header_size;
 
    m_BMPInfoHeader.Length=sizeof(InfoHead); 
    m_BMPInfoHeader.width=width;
    //BMP storage pixel data in opposite direction of Y-axis (from bottom to top).
    m_BMPInfoHeader.height=height;
    m_BMPInfoHeader.colorPlane=1;
    m_BMPInfoHeader.bitColor=24;
    m_BMPInfoHeader.realSize=3*width*height;
 
    fwrite(bfType,1,sizeof(bfType),fp_bmp);
    fwrite(&m_BMPHeader,1,sizeof(m_BMPHeader),fp_bmp);
    fwrite(&m_BMPInfoHeader,1,sizeof(m_BMPInfoHeader),fp_bmp);
 
    //BMP save R1|G1|B1,R2|G2|B2 as B1|G1|R1,B2|G2|R2
    //It saves pixel data in Little Endian
    //So we change 'R' and 'B'
    for(j =0;j<height;j++){
        for(i=0;i<width;i++){
            char temp=rgb24_buffer[(j*width+i)*3+2];
            rgb24_buffer[(j*width+i)*3+2]=rgb24_buffer[(j*width+i)*3+0];
            rgb24_buffer[(j*width+i)*3+0]=temp;
        }
    }
    fwrite(rgb24_buffer,3*width*height,1,fp_bmp);
    fclose(fp_rgb24);
    fclose(fp_bmp);
    free(rgb24_buffer);
    printf("Finish generate %s!\n",bmppath);
    return 0;
}
 
/**
 * Generate Picture contains Gray Bar changing from Black to White in YUV422P Format
 *
 * @param width     the width of picture.
 * @param height    the height of picture.
 * @param barnum    the number of Bars in the picture.
 * @param ymin      the minimum value of luminance.
 * @param ymax      the maximum value of luminance.
 * @return 0 if finished, -1 if there are errors.
 */
int gen_yuv420p_graybar(int width, int height,int barnum,unsigned char ymin,unsigned char ymax){
 
    int barwidth;
    float lum_inc;
    unsigned char lum_temp;
    int uv_width,uv_height;
    FILE *fp=NULL, *fp1;
    unsigned char *data_y=NULL;
    unsigned char *data_u=NULL;
    unsigned char *data_v=NULL;
    int t=0,i=0,j=0;
    char filename[100]={0};
 
    //Check
    if(width<=0||height<=0||barnum<=0){
        printf("Error: Width, Height or Bar Number cannot be 0 or negative number!\n");
        printf("Default Param is used.\n");
        width=640;
        height=480;
        barnum=10;
    }
    if(width%barnum!=0){
        printf("Warning: Width cannot be divided by Bar Number without remainder!\n");
    }
    barwidth=width/barnum;
    lum_inc=((float)(ymax-ymin))/((float)(barnum-1));
    uv_width=width/2;
    uv_height=height/2;
 
    data_y=(unsigned char *)malloc(width*height);
    data_u=(unsigned char *)malloc(uv_width*uv_height);
    data_v=(unsigned char *)malloc(uv_width*uv_height);
 
    sprintf(filename,"graybar_%dx%d_yuv420p.yuv",width,height);
    if((fp=fopen(filename,"wb+"))==NULL){
        printf("Error: Cannot create file!");
        return -1;
    }
    sprintf(filename,"graybar_%dx%d.rgb",width,height);
    if((fp1=fopen(filename,"wb+"))==NULL){
        printf("Error: Cannot create file!");
        return -1;
    }
 
    //Output Info
    printf("Y, U, V value from picture's left to right:\n");
    for(t=0;t<(width/barwidth);t++){
        lum_temp=ymin+(char)(t*lum_inc);
        printf("%3d, 128, 128\n",lum_temp);
    }
    //Gen Data
    for(j=0;j<height;j++){
        for(i=0;i<width;i++){
            t=i/barwidth;
            lum_temp=ymin+(char)(t*lum_inc);
            data_y[j*width+i]=lum_temp;
        }
    }
    for(j=0;j<uv_height;j++){
        for(i=0;i<uv_width;i++){
            data_u[j*uv_width+i]=128;
        }
    }
    for(j=0;j<uv_height;j++){
        for(i=0;i<uv_width;i++){
            data_v[j*uv_width+i]=128;
        }
    }
    fwrite(data_y,width*height,1,fp);
    fwrite(data_u,uv_width*uv_height,1,fp);
    fwrite(data_v,uv_width*uv_height,1,fp);
    fclose(fp);
	// convert to rgb
    unsigned char *rgb = (unsigned char *)malloc(width*height*3);
	for(j=0;j<height;j++)
	{
        for(i=0;i<width;i++)
		{
			rgb[(j*width+i)*3 + 2] = data_y[j*width+i] 
				+ 1.402 * (data_u[(j/2)*width/2 + (i/2)] - 128);
  			rgb[(j*width+i)*3 + 1] = data_y[j*width+i]  
				- 0.34414 * (data_u[(j/2)*width/2 + (i/2)] - 128)
				- 0.71414 * (data_v[(j/2)*width/2 + (i/2)] - 128);
			rgb[(j*width+i)*3 + 0] = data_y[j*width+i] 
				+ 1.772 * (data_u[(j/2)*width/2 + (i/2)] - 128);
        }
    }



    fwrite(rgb,width*height*3,1,fp1);
    fclose(fp1);


    free(data_y);
    free(data_u);
    free(data_v);
	free(rgb);
    printf("Finish generate %s!\n",filename);
}
static void _Rgb2Hsv(float R, float G, float B, float *H, float *S, float *V)
{
#define  MIN(A,B) ((A<B)?A:B)
#define  MAX(A,B) ((A>B)?A:B)
	// r,g,b values are from 0 to 1
	// h = [0,360], s = [0,1], v = [0,1]
	// if s == 0, then h = -1 (undefined)

	#define UNDEFINEDCOLOR 0
	float min, max, delta,tmp;
	tmp = MIN(R, G);
	min = MIN( tmp, B );
	tmp = MAX( R, G);
	max = MAX(tmp, B );
	*V = max; // v

	delta = max - min;

	if( max != 0 )
		*S = delta / max; // s
	else
	{
		// r = g = b = 0 // s = 0, v is undefined
		*S = 0;
		*H = UNDEFINEDCOLOR;
		return;
	}
	if( R == max )
		*H = ( G - B ) / delta; // between yellow & magenta
	else if( G == max )
		*H = 2 + ( B - R ) / delta; // between cyan & yellow
	else
		*H = 4 + ( R - G ) / delta; // between magenta & cyan
	*H *= 60; // degrees
	if( *H < 0 )
		*H += 360;
}

void yuv2hsv(char *rgbfile, char *hsvfile, int width, int height)
{
    FILE *fp_yuv=NULL, *fp_hsv, *fp_rgb;
    unsigned char *rgb_buffer=NULL;
	// float  char *hsv_buffer = NULL;
	unsigned  char *hsv_buffer = NULL;
	unsigned  char *yuv_buffer = NULL;
	float _h, _s, _v;
	int i,j;
    if((fp_yuv=fopen(rgbfile,"rb"))==NULL){
        printf("Error: Cannot open input RGB24 file:%s\n", rgbfile);
        return ;
    }
    if((fp_hsv=fopen(hsvfile,"wb"))==NULL){
        printf("Error: Cannot open output BMP file.\n");
        return ;
    }
    if((fp_rgb=fopen("rgb.rgb","wb"))==NULL){
        printf("Error: Cannot open output BMP file.\n");
        return ;
    }
    hsv_buffer =(unsigned char *)malloc(width*height*3);
    rgb_buffer=(unsigned char *)malloc(width*height*3);
    yuv_buffer=(unsigned char *)malloc(width*height*3/2);
    fread(yuv_buffer,1,width*height*3/2,fp_yuv);

	// convert to rgb
    unsigned char *data_y=NULL;
    unsigned char *data_u=NULL;
    unsigned char *data_v=NULL;
	data_y = yuv_buffer;
	data_u = data_y +  width*height;
	data_v = data_u + width/2*height/2;
	for(j=0;j<height;j++)
	{
        for(i=0;i<width;i++)
		{
			rgb_buffer[(j*width+i)*3 + 2] = data_y[j*width+i] 
				+ 1.402 * (data_u[(j/2)*width/2 + (i/2)] - 128);
  			rgb_buffer[(j*width+i)*3 + 1] = data_y[j*width+i]  
				- 0.34414 * (data_u[(j/2)*width/2 + (i/2)] - 128)
				- 0.71414 * (data_v[(j/2)*width/2 + (i/2)] - 128);
			rgb_buffer[(j*width+i)*3 + 0] = data_y[j*width+i] 
				+ 1.772 * (data_u[(j/2)*width/2 + (i/2)] - 128);
        }
    }

	/// to hsv
	for(j = 0; j < height ; j++)
	{
        for(i = 0; i < width ; i++)
		{
			_Rgb2Hsv(rgb_buffer[(j*width + i)*3 + 2], 
					 rgb_buffer[(j*width + i)*3 + 1],
					 rgb_buffer[(j*width + i)*3 + 0],
					 &_h, &_s, &_v);
			
			/// 把hsv 通过rgb的方式转换出来
			hsv_buffer[(j*width + i)*3 + 2] = _h/256*256;
			hsv_buffer[(j*width + i)*3 + 1] = _h/256*256;
			hsv_buffer[(j*width + i)*3 + 0] = _h/256*256;
        }
    }
    fwrite(rgb_buffer,3*width*height,1,fp_rgb);
    fwrite(hsv_buffer,3*width*height,1,fp_hsv);
    fclose(fp_hsv);
    fclose(fp_yuv);
    fclose(fp_rgb);
    free(rgb_buffer);
    free(hsv_buffer);
    free(yuv_buffer);
}

int main ( int argc, char *argv[] )
{

    //All picture's resolution is 1280x720
    //Gray Bar, from 16 to 235
    // gen_yuv420p_graybar(1280,720,10,16,235);
    /// rgb24_to_bmp("graybar_1280x720.rgb","1280x720_rgb24.bmp",1280,720);
	// rgb2hsv("colorbar_1280x720_rgb24.rgb", "hsv.rgb", 1280, 720);
	yuv2hsv("1920_1080_p420.yuv", "hsv.rgb", 1920, 1080);
    rgb24_to_bmp("hsv.rgb","hsv.bmp",1920,1080);
    rgb24_to_bmp("rgb.rgb","rgb.bmp",1920,1080);

	printf("ok\n");
	return EXIT_SUCCESS;
}				/* ----------  end of function main  ---------- */
